package controllers;

import models.*;
import notifiers.Mails;

import play.libs.Mail;
import play.mvc.Controller;
import play.mvc.With;

import java.util.Calendar;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import javax.persistence.PersistenceException;

import com.google.gson.JsonArray;
import com.google.gson.JsonObject;

/*
 @CRUD.For(Task.class)
 public class Tasks extends CRUD{*/

@With(Secure.class)
public class Tasks extends Controller {

	public static void index() {
		List<Task> allTasks = Task.all().fetch();
		List<TaskType> taskTypes = TaskType.getAllTaskTypes();
		List<TaskStatus> taskStatuses = TaskStatus.getAllTaskStatuses();
		render(allTasks, taskTypes, taskStatuses);
	}

	/**
	 * Calls the method which assigns the currently logged user to the task,
	 * then redirects to the page of all project tasks
	 * 
	 * @param projectId
	 * @param taskId
	 * 
	 */
	public static void renderAssignLoggedToTask(long taskId, long projectId) {

		Task task = Task.findById(taskId);
		if (!task.assignLoggedUserToTask(taskId))
			flash.error(" You are either trying to assign yourself to a task that you are reviewing "
					+ "or requested to review ");

		showAllProjectTasks(projectId);
	}

	/**
	 * Renders and calls the page of assigning a reviewer to a certain task in a
	 * project
	 * 
	 * @param projectId
	 * @param taskId
	 */
	public static void showAssignReviewer(long projectId, long taskId) {
		Task task = Task.findById(taskId);
		Project project = Project.findById(projectId);
		long userId = Long.parseLong(session.get("userId"));
		List<User> reviewers = task.getProjectReviewers(projectId, userId);
		render(reviewers, task, project);
	}

	/**
	 * Calls the method assignReviewer and redirects to show all the tasks in
	 * the project with the newly accepted reviewer assigned to review the task.
	 * 
	 * @param projectId
	 * @param taskId
	 * @param reviewerId
	 */
	public static void assignTaskReviewer(long taskId, long reviewerId,
			long projectId) {
		Task task = Task.findById(taskId);
		task.assignReviewer(reviewerId, projectId);

		User reviewer = User.findById(reviewerId);
		String subject = "Task Review";

		String msg = "You have been assigned to review the task \""
				+ task.description
				+ "\" because your request to review it's task type has been accepted.";
		Mail.send("coolsoft@SMT.com", reviewer.email, subject, msg);
		showAllProjectTasks(projectId);
	}

	/**
	 * Calls acceptReviewRequest() method and redirects to the review requests
	 * page
	 * 
	 * @param pendingReviewId
	 * 
	 * 
	 */
	public static void renderAcceptReviewRequest(long pendingReviewId) {
		TaskUser pendingReview = TaskUser.findById(pendingReviewId);
		Long projectId;
		if (pendingReview.task.events != null
				&& pendingReview.task.events.size() > 0)
			projectId = (Long) pendingReview.task.events.get(0).project
					.getEntityId();
		else
			projectId = (Long) pendingReview.task.userStory.component.project
					.getEntityId();

		pendingReview.acceptReviewRequest();
		showTaskReviewRequests(projectId);

	}

	/**
	 * Calls rejectReviewRequest() method and redirects to the review requests
	 * page
	 * 
	 * @param pendingReviewId
	 * 
	 * 
	 */
	public static void renderRejectReviewRequest(long pendingReviewId) {

		TaskUser pendingReview = TaskUser.findById(pendingReviewId);
		long projectId;
		if (pendingReview.task.events != null
				&& pendingReview.task.events.size() > 0)
			projectId = (Long) pendingReview.task.events.get(0).project
					.getEntityId();
		else
			projectId = (Long) pendingReview.task.userStory.component.project
					.getEntityId();
		pendingReview.rejectReviewRequest();
		showTaskReviewRequests(projectId);

	}

	/**
	 * This method takes the task i want to change its assignee and removes the
	 * previous and enters the new value
	 * 
	 * @param taskId
	 *            task id
	 * @param newValue
	 *            the new assignee user name ( as it is unique so i can search )
	 * @param proId
	 *            project id
	 * @return
	 */
	public static User editTaskAssignee(final long taskId,
			final String newValue, final long proId) {
		long userId = Long.parseLong(session.get("userId"));
		User inSession = User.findById(userId);
		Task task = Task.findById(taskId);
		User assignee = User.find("userName", newValue).first();
		Long taskUserUserId = TaskUserUser
				.find("select tuu.id from TaskUserUser tuu where tuu.task=? and tuu.status=?",
						task, true).first();
		if (taskUserUserId != null) {
			TaskUserUser tuu = TaskUserUser.findById(taskUserUserId);
			boolean edit = false;
			if (task.userStory == null) {
				ComponentUser x = new ComponentUser(true, assignee,
						task.userStory.component);
				if (task.userStory.component.user_components.contains(x))
					edit = true;
				else
					edit = false;
			} else {
				Project pro = Project.findById(proId);
				List<Project> projects = UserRoleProject
						.find("select distinct urp.project from UserRoleProject urp where urp.user=? and urp.project.status=true",
								assignee).fetch();
				if (projects.contains(pro))
					edit = true;
				else
					edit = false;
			}
			if (edit) {
				tuu.assignee = assignee;
				tuu.save();
				new Log(userId, (Long) task.getEntityId(), "Task",
						"Assignee to Task Changed", proId, " ").save();
				new Notification((Long) tuu.assignee.getEntityId(),
						"Assigned to Task", inSession.userName
								+ "You Are Assigned to Task "
								+ task.description + "By the Scrum Master"
								+ inSession.userName).save();
				Mails.editPB(tuu.assignee, "You Are Assigned to Task "
						+ task.description + "By the Scrum Master"
						+ inSession.userName, "Assigned to Task");
			} else
				flash.error("This user is not a member of this component");

		}

		return assignee;
	}

	/**
	 * this method to get the priorities list as JSON object to display on the
	 * drop down list
	 */
	public static void getPrioritiesJSON() {
		String[] pr = new String[getPriorities().size()];
		getPriorities().toArray(pr);
		renderJSON(pr);
	}

	/**
	 * Calls changeTaskStatusName() method and redirects to the TaskTypes and
	 * Statuses page
	 * 
	 * @param projectId
	 * @param id
	 * @param name
	 * @param isSystem
	 * 
	 * 
	 */
	public static void renderChangeTaskStatusName(long projectId, long id,
			String name, boolean isSystem) {
		TaskStatus status = TaskStatus.findById(id);
		if (!isSystem)
			Security.checkAccess(
					"Create/Edit/delete task status in my project", projectId,
					Project.class.getName());
		status.changeTaskStatusName(projectId, name, isSystem);
		if (isSystem)

			Tasks.index();
		else
			TaskTypes.index(projectId);
	}

	/**
	 * Calls deleteTaskStatus() method and redirects to the TaskTypes and
	 * Statuses page
	 * 
	 * @param projectId
	 * @param isSystem
	 * 
	 * 
	 */
	public static void renderDeleteTaskStatus(long id, boolean isSystem) {
		TaskStatus status = TaskStatus.findById(id);
		long projectId = -1;
		if (!isSystem) {

			projectId = (Long) status.project.getEntityId();
			Security.checkAccess(
					"Create/Edit/delete task status in my project", projectId,
					Project.class.getName());
		}
		status.delete();
		if (isSystem)
			index();
		else
			TaskTypes.index(projectId);

	}

	/**
	 * get all task that has no one assigned to
	 * 
	 * @param projectID
	 *            is the project id
	 */
	/*
	 * public static List<Task> getFreeTask(long projectID) { List<Task> tasks =
	 * getAllProjectTasks(projectID); List<Task> freeTasks = new
	 * LinkedList<Task>(); for (int i = 0; i < tasks.size(); i++) { // if
	 * (tasks.get(i).assignee == null) freeTasks.add(tasks.get(i)); } return
	 * freeTasks;
	 * 
	 * }
	 */
	/**
	 * assign developer to a task
	 * 
	 * @param userID
	 *            the userID
	 * @param taskID
	 *            the task id
	 */
	/*
	 * public static void assignDeveleporToTask(long userID, long taskID) {
	 * 
	 * Task t = Task.findById(taskID); User u = User.findById(userID); //
	 * t.assignee = u; t.save();
	 * 
	 * }
	 */

	public static void fillTotalEffort(long taskId, double effort) {
		Task task = Task.findById(taskId);
		String message = task.fillTotalEffort(effort);
		if (message == "estimation inserted successfully")
			flash.success(message);
		else
			flash.error(message);
		showTask(taskId);

	}

	public static void fillTotalEffortpage(long taskID) {
		Task t = Task.findById(taskID);

		render(t);

	}

	/**
	 * renders the form to create a new Task.
	 * 
	 * @author Rana Ashraf
	 * @param projectId
	 * 
	 */
	public static void showCreateTask(Long projectId) {
		Security.checkAccess("Create/edit/delete Task in my project",
				projectId, Project.class.getName());
		List<Event> availableEvents = Events.listAvailableEvents(projectId);
		List<Event> availableSprints = new LinkedList();

		if (availableEvents != null)
			for (int i = 0; i < availableEvents.size();) {
				if (availableEvents.get(i).type != null)
					if (availableEvents.get(i).type.type.equals("Sprint"))
						availableSprints.add(availableEvents.remove(i));
					else
						i++;

			}
		long userId = Long.parseLong(session.get("userId"));
		Component component = Users.getUserComponent(userId, projectId);
		List<UserStory> userStories = null;
		if (component != null) {
			userStories = UserStories.getComponentStories((Long) component
					.getEntityId());
		}
		Set<Task> tasks = new HashSet<Task>();
		Project project = Project.findById(projectId);
		tasks.addAll(project.getAllProjectTasks());
		List<TaskType> taskTypes = project.availableTaskTypes();
		List<TaskStatus> taskStatuses = project.availableTaskStatuses();
		TaskType impediment = TaskType
				.find("select tt from TaskType tt where tt.type.type=?",
						"Impediment").first();
		taskTypes.remove(impediment);
		TaskStatus closed = TaskStatus.find(
				"select ts from TaskStatus ts where ts.status.status=?",
				"CLOSED").first();
		TaskStatus resolved = TaskStatus.find(
				"select ts from TaskStatus ts where ts.status.status=?",
				"RESOLVED").first();
		TaskStatus fixed = TaskStatus.find(
				"select ts from TaskStatus ts where ts.status.status=?",
				"FIXED").first();
		taskStatuses.remove(fixed);
		taskStatuses.remove(closed);
		taskStatuses.remove(resolved);
		List<String> priorities = getPriorities();
		render(tasks, taskTypes, taskStatuses, userId, availableEvents,
				project, userStories, availableSprints, priorities);
	}

	/**
	 * returns a list of strings with the values HIGH, MEDIUM, LOW.
	 * 
	 * @author Rana Ashraf
	 * @return List<String>
	 */
	public static List<String> getPriorities() {
		List<String> priorities = new LinkedList<String>();
		priorities.add("High");
		priorities.add("Medium");
		priorities.add("Low");
		return priorities;
	}

	/**
	 * Renders the form to edit a specific task status.
	 * 
	 * @param taskId
	 */
	public static void showTaskStatus(long id) {
		TaskStatus status = TaskStatus.findById(id);
		Project project = status.project;
		render(status, project);
	}

	/**
	 * renders the form to edit a specific task.
	 * 
	 * @author Rana Ashraf
	 * @param taskId
	 * @param projectId
	 */
	public static void showEditTask(long taskId, long projectId) {
		Security.checkAccess("Create/edit/delete Task in my project",
				projectId, Project.class.getName());
		Task task = Task.findById(taskId);
		long userId = Long.parseLong(session.get("userId"));
		Component component = Users.getUserComponent(userId, projectId);
		List<UserStory> userStories = null;
		if (component != null) {
			userStories = UserStories.getComponentStories((Long) component
					.getEntityId());
		}
		List<Event> availableEvents = Events.listAvailableEvents(projectId);
		List<Event> availableSprints = new LinkedList();
		if (availableEvents != null) {
			for (int i = 0; i < availableEvents.size();) {
				if (availableEvents.get(i).type != null)
					if (availableEvents.get(i).type.type.equals("Sprint"))
						availableSprints.add(availableEvents.remove(i));
					else
						i++;
			}
		}
		List<Task> dependOn = task.dependOn;
		Set<Task> availableTasks = new HashSet<Task>();
		availableTasks.addAll(task.nonDependantTasks(projectId));
		List<TaskType> taskTypes = TaskType.getAllTaskTypes();
		List<TaskStatus> taskStatuses = TaskStatus.getAllTaskStatuses();
		User reviewer = task.getTaskReviewer(true);
		if (reviewer != null)
			if (userId != (Long) reviewer.getEntityId()) {
				TaskStatus closed = TaskStatus
						.find("select ts from TaskStatus ts where ts.status.status=?",
								"CLOSED").first();
				taskStatuses.remove(closed);
			}
		List<String> priorities = getPriorities();
		List<Event> associatedEvents = Events.getTaskEventsNotPast(task);
		if (availableEvents != null & !availableEvents.isEmpty()) {
			if (associatedEvents != null)
				availableEvents.removeAll(associatedEvents);
		}
		Event sprint = Events.getTaskSprintCurrent(task);
		boolean hasStory = false;
		if (task.userStory != null)
			hasStory = true;
		Project project = Project.findById(projectId);
		render(availableTasks, dependOn, taskTypes, taskStatuses, task,
				projectId, userStories, priorities, availableSprints,
				availableEvents, sprint, associatedEvents, hasStory, project);
	}

	/**
	 * This method is used to view all tasks in a specific project
	 * 
	 * @author Rana Ashraf
	 * @param projectId
	 */
	public static void showAllProjectTasks(Long projectId) {
		Set<Task> projectTasks = new HashSet<Task>();
		Project project = Project.findById(projectId);
		projectTasks.addAll(project.getAllProjectTasks());
		Long userId = Long.parseLong(session.get("userId"));
		User user = User.findById(userId);
		render(projectTasks, project, user);
	}

	/**
	 * This method is used to combine the creation of a task and viewing the
	 * form after the task is created
	 * 
	 * @author Rana Ashraf
	 * 
	 * @param description
	 * @param priority
	 * @param typeId
	 * @param statusId
	 * @param dependOn
	 * @param event
	 * @param projectId
	 */
	public static void renderCreateTask(String description, String details,
			String priority, long typeId, long statusId, Long[] dependOn,
			long[] eventsId, long projectId, long storyId, long sprintId) {
		long userId = Long.parseLong(session.get("userId"));
		Project project = Project.findById(projectId);
		boolean action1 = Security.checkPermission(
				"Create/edit/delete Task in my project", project);
		boolean action2 = Security.checkPermission("associate tasks to event",
				project);
		boolean action3 = Security.checkPermission(
				"Associate a task to a specific sprint in my project", project);
		boolean action4 = Security.checkPermission(
				"Set/edit priority of a task", project);
		boolean action5 = Security.checkPermission(
				"associate a task to a user story", project);
		boolean action6 = Security.checkPermission(
				"Set/edit dependances between tasks", project);
		if (!(action1 || action2 || action3 || action4 || action5 || action6))
			forbidden("Access Denied! You are not authorized to view this page!");

		models.Task.createTask(description, details, priority, typeId,
				statusId, dependOn, eventsId, projectId, storyId, sprintId,
				userId);
		flash.success("Your Task has been created successfully!");

		showAllProjectTasks(projectId);
		// showCreateTask(projectId);
	}

	/**
	 * This method is used to combine editing a task and viewing the form after
	 * the task is edited
	 * 
	 * @author Rana Ashraf
	 * 
	 * @param description
	 * @param priority
	 * @param typeId
	 * @param statusId
	 * @param dependOn
	 * @param taskId
	 * @param projectId
	 */
	public static void renderEditTask(String description, String details,
			String priority, long typeId, long statusId, long[] dependOn,
			long taskId, long projectId, long storyId, long[] eventsId,
			long sprintId, String userComment) {
		long userId = Long.parseLong(session.get("userId"));
		Project project = Project.findById(projectId);
		boolean action1 = Security.checkPermission(
				"Create/edit/delete Task in my project", project);
		boolean action2 = Security.checkPermission("associate tasks to event",
				project);
		boolean action3 = Security.checkPermission(
				"Associate a task to a specific sprint in my project", project);
		boolean action4 = Security.checkPermission(
				"Set/edit priority of a task", project);
		boolean action5 = Security.checkPermission(
				"associate a task to a user story", project);
		boolean action6 = Security.checkPermission(
				"Set/edit dependances between tasks", project);
		if (!(action1 || action2 || action3 || action4 || action5 || action6))
			forbidden("Access Denied! You are not authorized to view this page!");
		Task task = Task.findById(taskId);
		String temp = task.description;
		Task.editTask(description, details, priority, typeId, statusId,
				dependOn, taskId, projectId, storyId, eventsId, sprintId,
				userId, userComment);

		User editor = null;
		if (userId != (Long) task.creator.getEntityId()) {
			editor = User.findById(userId);

			if (editor != null && !task.type.type.equals("Impediment")) {
				Mails.notifyEditTask(task.creator, editor, task, temp);

			}

		}
		checkTaskType(typeId, task, projectId, editor, userComment, userId);

		flash.success("Task has been edited successfully!");

		showTask(taskId);
	}

	/**
	 * returns true. Given the type of a task, if impediment the assignee of
	 * that task is unassigned and is notified by mail. All uses who have the
	 * permission to be assigne to a task of type impediment are notified as
	 * well
	 * 
	 * @param type
	 * @param task
	 * @param projectId
	 * @param editor
	 * @param userComment
	 * @param userId
	 * @return boolean
	 */
	public static boolean checkTaskType(Long typeId, Task task, Long projectId,
			User editor, String userComment, Long userId) {
		TaskUserUser taskUserUser = TaskUserUser
				.find("select tuu from TaskUserUser tuu where tuu.task=? and tuu.status=?",
						task, true).first();
		TaskType type = TaskType.findById(typeId);
		if (type.type.equals("Impediment")) {
			List<User> users = Users.getUsersWithPermission(
					"Become assigned to impediment tasks", projectId);
			if (users != null)
				Notifications.notifyAllUsers(users, "Impediment Task",
						editor.firstName + " " + editor.lastName
								+ " has changed the type of the task "
								+ task.description + " to impediment");
			new Log(userId, (Long) task.getEntityId(), "Task",
					"changed the task type to impediment", projectId,
					userComment).save();
			User assignee = task.getTaskAssignee();
			if (assignee != null) {
				Mails.notify(
						assignee,
						"You have become unassigned",
						"You have been unassigned from the task "
								+ task.description
								+ " as you changed the type of the task to impediment",
						"/tasks/showtask?taskId=" + (Long) task.getEntityId());
				new Notification(
						(Long) assignee.getEntityId(),
						"Unassigned",
						"You have been unassigned from the task "
								+ task.description
								+ " as you changed the type of the task to impediment")
						.save();
				taskUserUser.assignee = null;
				taskUserUser.save();

			}
			for (int i = 0; i < users.size(); i++) {
				Mails.notify(users.get(i), "Impediment Task", editor.firstName
						+ " " + editor.lastName
						+ " has changed the type of the task "
						+ task.description + " to impediment.",
						"/tasks/showtask?taskId=" + (Long) task.getEntityId());
			}
			TaskStatus status = TaskStatus.find("byStatus", "NEW").first();
			task.status = status;
			task.save();
			return true;
		}
		return false;
	}

	/**
	 * Renders the page to view all details of a task
	 * 
	 * @author Rana Ashraf
	 * @param taskId
	 */
	public static void showTask(long taskId) {
		Task task = Task.findById(taskId);
		Project project = task.events.get(0).project;
		List<Task> dependOn = null;
		List<Task> dependants = null;
		if (!task.dependOn.isEmpty()) {
			dependOn = task.dependOn;
		}
		if (!task.dependants.isEmpty())
			dependants = task.dependants;
		List<Event> events = task.getTaskEvents();
		Event sprint = null;
		if (events != null)
			for (int i = 0; i < events.size();) {
				if (events.get(i).type != null)
					if (events.get(i).type.type.equals("Sprint"))
						sprint = events.remove(i);
					else {
						if (events.get(i).type.type.equals("Default")) {
							events.remove(i);

						} else
							i++;
					}

			}
		User reviewer = task.getTaskReviewer(true);
		long userId = Long.parseLong(session.get("userId"));
		boolean isReviewer = false;
		boolean isAssignee = false;
		boolean isResolved = false;
		User assignee = task.getTaskAssignee();
		User user = User.findById(userId);
		if (task.status.status.equals("RESOLVED"))
			isResolved = true;
		if (user.equals(reviewer))
			isReviewer = true;
		User creator = null;
		if (userId == (Long) task.creator.getEntityId())
			creator = User.findById(userId);
		render(task, dependOn, events, project, sprint, dependants, reviewer,
				creator, isReviewer, isResolved);
	}

	/**
	 * combines the method to request to review a task and rendering the page
	 * displaying all tasks in the project after the request is sent
	 * 
	 * @param taskId
	 */
	public static void renderRequestReviewTask(long taskId) {

		Long userId = Long.parseLong(session.get("userId"));
		User reviewer = User.findById(userId);
		Task task = Task.findById(taskId);
		Security.checkAccess("review a task", taskId, Task.class.getName());
		List<User> users = task.requestReviewTask(userId);
		for (int i = 0; i < users.size(); i++) {
			Mails.notify(users.get(i), "A request to review",
					reviewer.firstName + " " + reviewer.lastName
							+ " has requested to review the task "
							+ task.description, "/tasks/showtask?taskId="
							+ task.getEntityId());
		}
		flash.success("Your request has been successfully sent!");

		Project p = task.events.get(0).project;
		showAllProjectTasks((Long) p.getEntityId());
	}

	/**
	 * renders the pending task review requests of a specific project
	 * 
	 * 
	 * @param projectId
	 */
	public static void showTaskReviewRequests(Long projectId) {
		Project project = Project.findById(projectId);
		boolean canDoAction1 = Security.checkPermission(
				"approve requests by developers to be reviewers", project);
		boolean canDoAction2 = Security.checkPermission(
				"approve task type review requests", project);
		if (!canDoAction1 && !canDoAction2) {
			forbidden("Access Denied! You are not authorized to view this page!");
		}
		Long userId = Long.parseLong(session.get("userId"));
		User user = User.findById(userId);
		List<TaskUser> pendingReviews = project.getPendingTaskReviewRequests();
		if (pendingReviews != null)
			for (int i = 0; i < pendingReviews.size();) {
				User userRequesting = pendingReviews.get(i).reviewer;
				if (userRequesting != null) {
					if (userRequesting.equals(user))
						pendingReviews.remove(i);
					else
						i++;
				} else
					i++;
			}

		List<ComponentTaskTypeUser> projectTaskTypeReviews = project
				.getPendingProjectReviewRequests();
		if (projectTaskTypeReviews != null)
			for (int i = 0; i < projectTaskTypeReviews.size();) {
				User userRequesting2 = projectTaskTypeReviews.get(i).reviewer;
				if (userRequesting2 != null) {
					if (userRequesting2.equals(user))
						projectTaskTypeReviews.remove(i);
					else
						i++;
				} else
					i++;
			}
		List<ComponentTaskTypeUser> componentTaskTypeReviews = project
				.getPendingComponentReviewRequests();
		if (componentTaskTypeReviews != null)
			for (int i = 0; i < componentTaskTypeReviews.size();) {
				User userRequesting3 = componentTaskTypeReviews.get(i).reviewer;
				if (userRequesting3 != null) {
					if (userRequesting3.equals(user))
						componentTaskTypeReviews.remove(i);
					else
						i++;
				} else
					i++;
			}
		render(pendingReviews, projectTaskTypeReviews,
				componentTaskTypeReviews, project);
	}

	/**
	 * this method to change the status of the task and notify reviewer,
	 * assignee and creator. it render JSON Array of true if success else JSON
	 * array false.
	 * 
	 * @param taskId
	 *            Id of task changed
	 * @param newStatusId
	 *            the Id of the new status
	 * @return boolean
	 * @see TaskStatus,Task
	 * 
	 */

	public static boolean changeTaskStatus(long taskId, long newStatusId) {

		// to get the task and status
		TaskStatus status = TaskStatus.findById(newStatusId);
		Task task = Task.findById(taskId);

		// get the user
		long userId = Long.parseLong(session.get("userId"));
		User doneBy = User.findById(userId);

		// check if he has access
		boolean haveAcc = Security.checkPermission("Drag and Drop Tasks",
				task.events.get(0).project);

		// get the reviewer and Assignee
		User reviewer = task.getTaskReviewer(true);
		User Assignee = task.getTaskAssignee();

		boolean isMaster = Security.checkPermission("Master Board",
				task.events.get(0).project);

		boolean closeByReviewer = status.status.equals("CLOSED")
				&& reviewer != null
				&& reviewer.getEntityId().equals(doneBy.getEntityId());

		boolean isAssignee = (Assignee == null) ? false : doneBy.getEntityId()
				.equals(Assignee.getEntityId());

		// if he is a master or reviewer want to close or assignee but will not
		// close.

		if (haveAcc
				&& (isMaster || closeByReviewer || (!status.status
						.equals("CLOSED") && isAssignee))) {
			changeStatusHelper(task, status);
			if (task.userStory != null) {
				task.userStory.checkStoryStatus((Long) status.getEntityId(),
						(Long) task.getEntityId());
			}
			// log and notify
			new Log(userId, (Long) task.getEntityId(), (Task.class.getName()),
					"changed status to " + status.status, null).save();
			notifyChangesInTask(doneBy, task, "changed the task status to "
					+ task.status.status);
			boolean[] error = new boolean[] { true };
			renderJSON(error);
			return true;
		} else {
			boolean[] error = new boolean[] { false };
			renderJSON(error);
			return false;
		}

	}

	public static boolean changeStatusHelper(Task t, TaskStatus s) {
		t.status = s;
		t.save();
		return true;
	}

	/**
	 * to notify the reviewer creator and assignee about any change in the task
	 * 
	 * @param doneBy
	 *            user did the change
	 * @param t
	 *            the task changed
	 * @param message
	 *            the message that appears in notification and mail
	 * @return
	 */
	private static boolean notifyChangesInTask(User doneBy, Task task,
			String message) {
		User reviewer = task.getTaskReviewer(true);
		User assignee = task.getTaskAssignee();
		List<User> users = new LinkedList();
		if (task.creator != null
				&& !doneBy.getEntityId().equals(task.creator.getEntityId()))
			users.add(task.creator);

		if (reviewer != null
				&& !doneBy.getEntityId().equals(reviewer.getEntityId()))
			users.add(reviewer);
		if (assignee != null
				&& !doneBy.getEntityId().equals(assignee.getEntityId()))
			users.add(assignee);
		for (int i = 0; i < users.size(); i++) {
			Mails.notifyEditTask(users.get(i), doneBy, task, message);
		}
		Notifications.notifyAllUsers(users, "Task Edited",
				(doneBy.userName + message));

		return true;
	}

	/**
	 * renders tasks that has no one assigned in a project to and developers in
	 * the same project
	 * 
	 * @param projectID
	 *            the project ID
	 */
	public static void assignDeveloperToTask(long projectId) {

		Project project = Project.findById(projectId);
		List<Task> freeTasks = project.getFreeTask();
		List<User> devInProject = Users.getDeveloperInProject(projectId);

		render(freeTasks, devInProject);

	}

	/**
	 * assign developer to a task and notify him by mail
	 * 
	 * @param userID
	 *            the userID
	 * @param taskID
	 *            the task id
	 */
	public static void assignDeveloperToTaskAction(long userId, long taskId) {
		Long assignerId = Long.parseLong(session.get("userId"));
		User assigner = User.findById(assignerId);
		Task task = Task.findById(taskId);
		User user = User.findById(userId);
		task.assignDeveloperTask(userId, assignerId);
		flash.success("Developer " + user.firstName + " Assigned "
				+ task.description + " successully ");

		Mails.notify(user, "You have been assigned to a new task",
				assigner.firstName + " " + assigner.lastName
						+ "has assigned you to work on " + task.description,
				"http://localhost:9000/tasks/showtask?taskId=" + task.id);
		Projects.showProject(task.events.get(0).project.id);

	}

	/**
	 * renders tasks that has no one assigned in a project to and developers in
	 * the same project
	 * 
	 * @param projectID
	 *            the project ID
	 */
	public static void showSuggestDeveloperToTask(long projectId) {

		Project project = Project.findById(projectId);

		List<Task> freeTasks = project.getFreeTask();
		List<User> devInProject = Users.getDeveloperInProject(projectId);

		User myself = User.findById(Long.parseLong(session.get("userId")));

		devInProject.remove(myself);
		render(freeTasks, devInProject, project);

	}

	/**
	 * suggest a task to a developer and notifying him by mail
	 * 
	 * @param userID
	 *            the userID
	 * @param taskID
	 *            the task id
	 */
	public static void suggestDeveloperToTask(long userId, long taskId) {
		Long suggesterId = Long.parseLong(session.get("userId"));

		Task task = Task.findById(taskId);
		User user = User.findById(userId);
		User suggester = User.findById(suggesterId);

		System.out.println("TaskId " + taskId);
		List<TaskUserUser> LTUU = task.getUserTaskRelation(user);
		if (LTUU != null)
			for (int i = 0; i < LTUU.size(); i++) {
				if (LTUU.get(i).user != null) {
					flash.error("this user is allready suggested to work on this task");
					Projects.showProject(task.events.get(0).project.id);

				}

			}
		task.suggestDeveloperToTask(userId, suggesterId);
		flash.success("Developer " + suggester.firstName + " suggested "
				+ task.description + " successully ");

		Mails.notify(user, "You have been suggested to a new task",
				suggester.firstName + " " + suggester.lastName
						+ "has suggested you to work on " + task.description,
				"http://localhost:9000/tasks/showtask?taskId=" + task.id);
		Projects.showProject(task.events.get(0).project.id);

	}

	/**
	 * 
	 * calls the method that assigns a user as a reviewer to a specific task
	 * then renders the page of all tasks with a success message
	 * 
	 * @author Rana Ashraf
	 * @param taskId
	 */
	public static void renderReviewTask(long taskId) {

		Long userId = Long.parseLong(session.get("userId"));
		Task task = Task.findById(taskId);
		Project project = task.events.get(0).project;
		Security.checkAccess("assign myself as a reviewer",
				(Long) project.getEntityId(), Project.class.getName());
		task.reviewTask(userId, (Long) project.getEntityId());

		flash.success("You have been assigned to review the task");
		showAllProjectTasks((Long) project.getEntityId());
	}

	/**
	 * this method checks if the user is assigned to the task is the same one
	 * logged ins
	 * 
	 * 
	 * @param userID
	 * @param taskID
	 * @return
	 */
	public static boolean sameDevloper(long taskId) {
		Task task = Task.findById(taskId);
		User ut = task.getTaskAssignee();

		User u = User.findById(Long.parseLong(session.get("userId")));

		if (ut != null && ut.equals(u))
			return true;
		else
			return false;

	}

	/**
	 * renders user suggested tasks
	 * 
	 * @param userID
	 */
	public static void mySuggestedTasks() {
		List<Task> tasks = new LinkedList<Task>();

		User u = User.findById(Long.parseLong(session.get("userId")));

		tasks = u.getMySuggestedTasks();
		render(tasks);

	}

	/**
	 * change the description of the task and sends notification for creator,
	 * viewer and assignee
	 * 
	 * @param taskId
	 *            the changed task id
	 * @param newDiscription
	 *            the new description of the task
	 * @return boolean
	 * @author MHMGad
	 */
	public static boolean editTaskDescription(String taskId,
			String newDiscription) {
		Task task = Task.findById(Long.parseLong(taskId));
		if (newDiscription != null && !newDiscription.equals("")
				&& newDiscription.length() < 400) {

			User reviewer = task.getTaskReviewer(true);
			task.description = newDiscription;
			Security.checkAccess("Edit Task", ((Long) task.events.get(0)
					.getEntityId()).longValue(), Event.class.getName());
			task.save();
			long userId = Long.parseLong(session.get("userId"));
			User doneBy = User.findById(userId);

			// log and notify
			new Log(userId, (Long) task.getEntityId(), (Task.class.getName()),
					"changed the description of the task to "
							+ task.description, null).save();
			Tasks.notifyChangesInTask(doneBy, task,
					"changed the description of the task to "
							+ task.description);

			boolean[] error = new boolean[] { true };
			renderJSON(error);
			return true;
		} else {
			boolean[] error = new boolean[] { false };
			renderJSON(error);
			return false;
		}
	}

	public static void deleteTask(long taskId, long projectId) {
		Task task = Task.findById(taskId);
		Long userId = Long.parseLong(session.get("userId"));
		User user = User.findById(userId);
		User assignee = task.getTaskAssignee();
		User reviewer = task.getTaskReviewer(true);
		List<User> usersToBeNotified = new LinkedList<User>();
		if (assignee != null && !assignee.equals(user))
			usersToBeNotified.add(assignee);
		if (reviewer != null && !assignee.equals(user))
			usersToBeNotified.add(reviewer);
		for (int i = 0; i < usersToBeNotified.size(); i++) {
			Mails.notifyDeleteTask(usersToBeNotified.get(i), user, task);

		}
		/*
		 * Notifications.notifyAllUsers(usersToBeNotified, "Task Deleted",
		 * user.firstName + " " + user.lastName + " has deleted the task " +
		 * task.description);
		 */
		task.delete();
		System.out.println("has been deleted");
		showAllProjectTasks(projectId);
	}

	/**
	 * Is called after a reviewer sends a comment to the assignee and directs
	 * the reviewer the task page and a success message appears
	 * 
	 * @author Rana Ashraf
	 * @param taskId
	 * @param comment
	 */
	public static void submitReview(long taskId, String subject, String comment) {

		Task task = Task.findById(taskId);
		User user = task.getTaskAssignee();
		Mails.notifyTaskReview(user, task, subject, comment);
		flash.success("Your comment has been sent!");
		showTask(taskId);
	}

	/**
	 * renders the form in which a reviewer of a task can send a comment to the
	 * assignee
	 * 
	 * @author Rana Ashraf
	 * @param taskId
	 */
	public static void showReviewTask(long taskId) {

		Security.checkAccess("Create/edit/delete Task in my project", taskId,
				Task.class.getName());
		Task task = Task.findById(taskId);
		User assignee = task.getTaskAssignee();
		render(task, assignee);
	}

	/**
	 * accept's a given suggested task
	 * 
	 * @param taskID
	 */
	public static void acceptSuggestedTask(long taskId) {
		User u = User.findById(Long.parseLong(session.get("userId")));
		Task t = Task.findById(taskId);

		assignDeveloperToTaskAction(u.id, t.id);

	}

	/**
	 * reject's a given suggested task
	 * 
	 * @param taskID
	 */
	public static void rejectSuggestedTask(long taskId) {
		Task task = Task.findById(taskId);
		task.rejectSuggestedTask(Long.parseLong(session.get("userId")));
		flash.success(task.description + " was rejected");
		mySuggestedTasks();
	}

	public static User renderGetTaskAssignee(long taskId) {
		Task task = Task.findById(taskId);
		return task.getTaskAssignee();

	}

	public static User renderGetTaskReviewer(long taskId) {
		Task task = Task.findById(taskId);
		return task.getTaskReviewer(true);
	}

	public static boolean renderHasRequestedReview(long taskId) {
		long userId = Long.parseLong(session.get("userId"));
		User user = User.findById(userId);
		return user.hasRequestedReview(taskId);
	}

	/**
	 * Calls createTaskStatus() method and redirects to the TaskTypes and
	 * Statuses page
	 * 
	 * @param projectId
	 * @param name
	 * @param isSystem
	 * 
	 * 
	 */
	public static void renderCreateTaskStatus(long projectId, String name,
			boolean isSystem) {
		if (!isSystem)
			Security.checkAccess(
					"Create/Edit/delete task status in my project", projectId,
					Project.class.getName());
		TaskStatus.createTaskStatus(projectId, name, isSystem);
		if (isSystem)
			Tasks.index();
		else
			TaskTypes.index(projectId);
	}

	/**
	 * to change the priority of the task through board
	 * 
	 * @param taskId
	 *            changed taskId
	 * @param priority
	 *            the new priority (High-Meduim-Low)
	 * @return boolean
	 * @author MHMGad
	 */
	public static boolean editPriority(String taskId, String priority) {
		Security.checkAccess("Edit Task", Long.parseLong(taskId),
				Task.class.getName());
		Task s = Task.findById(Long.parseLong(taskId));
		User reviewer = s.getTaskReviewer(true);
		s.priority = priority;
		s.save();
		// log and notify
		long userId = Long.parseLong(session.get("userId"));
		User doneBy = User.findById(userId);
		new Log(userId, (Long) s.getEntityId(), (Task.class.getName()),
				"change the Priority of the task to " + s.priority, null)
				.save();
		Tasks.notifyChangesInTask(doneBy, s,
				"change the Priority of the task to " + s.priority);
		// end of notifing.

		return true;
	}

}